﻿using Dapper;
using LiJ.Common.Core;
using LiJ.Common.Core.Enums;
using LiJ.Common.Plugs.LogPlug;
using LiJ.Orm.Extention;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using LiJ.Common.Core.Utils;

namespace LiJ.Orm
{
    /// <summary>
    /// 仓储层基类
    /// </summary>
    public class BaseRepository
    {
        /// <summary>
        /// 数据库表名
        /// </summary>
        protected string SourceTableName;

        protected readonly string WriteConnectionString;
        protected readonly string ReaderConnectionString;
        protected readonly string ProviderName;

        public BaseRepository(string writeConnectionString = null, string readerConnectionString = null, string providerName = null)
        {
            this.ProviderName = providerName ?? ConfigurationUtil.GetSection("DbConnectionType").Value;
            this.WriteConnectionString = writeConnectionString ?? ConfigurationUtil.GetConnectionString("WriteConnection");
            this.ReaderConnectionString = readerConnectionString ?? ConfigurationUtil.GetConnectionString("ReadeConnection");
        }

        #region 底层基础读写分离

        /// <summary>
        /// 执行写数据库操作
        /// </summary>
        /// <typeparam name="RType">动态类型</typeparam>
        /// <param name="func">方法和连接</param>
        /// <returns></returns>
        protected internal async Task<RType> ExecuteWriteAsync<RType>(Func<IDbConnection, Task<RType>> func)
            where RType : ResultMo, new() => await Execute(func, WriteConnectionString, ProviderName);

        /// <summary>
        /// 执行读操作，返回具体类型，自动包装成ResultMo结果实体
        /// </summary>
        /// <typeparam name="RType">动态类型</typeparam>
        /// <param name="func">方法和连接字符串</param>
        /// <returns></returns>
        protected internal async Task<ResultMo<RType>> ExecuteReadAsync<RType>(Func<IDbConnection, Task<RType>> func) =>
            await Execute(async connection =>
            {
                var result = await func(connection);
                return result != null
                    ? new ResultMo<RType>(result)
                    : new ResultMo<RType>(ResultTypes.ObjectNull, "未发现相关数据！");
            }, ReaderConnectionString, ProviderName);

        /// <summary>
        /// 执行读操作，直接继承自ResultMo实体
        /// </summary>
        /// <typeparam name="RType">动态类型</typeparam>
        /// <param name="func">方法和连接字符串</param>
        /// <returns></returns>
        protected internal async Task<RType> ExecuteReadResultAsync<RType>(Func<IDbConnection, Task<RType>> func)
            where RType : ResultMo, new() => await Execute(func, ReaderConnectionString, ProviderName);

        /// <summary>
        /// 执行操作
        /// </summary>
        /// <typeparam name="RType">动态类型</typeparam>
        /// <param name="func">方法和链接池</param>
        /// <param name="connectionString">连接数据库字符串</param>
        /// <param name="providerName"></param>
        /// <returns></returns>
        private static async Task<RType> Execute<RType>(Func<IDbConnection, Task<RType>> func, string connectionString, string providerName)
            where RType : ResultMo, new()
        {
            RType t;
            try
            {
                LogUtil.Info(string.Concat("数据库类型：", providerName, "数据库连接字符串：", connectionString));
                var connection = DbProviderFactories.GetFactory(providerName).CreateConnection();
                connection.ConnectionString = connectionString;
                t = await func(connection);
            }
            catch (Exception e)
            {
                #if DEBUG
                throw e;
                #else
                LogUtil.Error(string.Concat("数据库操作错误，详情：", e.Message, "\r\n", e.StackTrace), "DataRepositoryConnectionError", "DapperRepository");
                t = new RType
                {
                    Ret = (int)ResultTypes.InnerError,
                    Msg = "数据更新出错！"
                };
                return t;
                #endif
            }

            return t ?? new RType() {Ret = (int) ResultTypes.ObjectNull, Msg = "未发现对应结果响应！"};
        }

        #endregion

        #region 基础CRUD操作方法

        /// <summary>
        /// 插入数据（默认Id自增长）
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="model">动态实体</param>
        /// <param name="isAuto">Id主键是否自增长</param>
        /// <returns></returns>
        public virtual async Task<ResultIdMo> Insert<T>(T model, bool isAuto = true) =>
            await ExecuteWriteAsync(connection => connection.Insert(model, SourceTableName, isAuto));

        /// <summary>
        /// 全量更新
        /// </summary>
        /// <typeparam name="TType">动态类型</typeparam>
        /// <param name="model">动态实体类</param>
        /// <param name="whereExpression">判断条件</param>
        /// <returns></returns>
        public virtual async Task<ResultMo> UpdateAll<TType>(TType model,
            Expression<Func<TType, bool>> whereExpression = null) => await ExecuteWriteAsync(connection =>
            connection.UpdateAll(model, whereExpression, SourceTableName));

        /// <summary>
        /// 部分字段的更新
        /// </summary>
        /// <typeparam name="TType">动态类型</typeparam>
        /// <param name="model">动态实体类</param>
        /// <param name="updateExpression">更新字段new{m.Name,....} Or new{m.Name="",....}</param>
        /// <param name="whereExpression">判断条件，如果为空默认根据Id判断</param>
        /// <returns></returns>
        public virtual async Task<ResultMo> Update<TType>(TType model, Expression<Func<TType, object>> updateExpression,
            Expression<Func<TType, bool>> whereExpression = null) => await ExecuteWriteAsync(connection =>
            connection.UpdatePartail(model, updateExpression, SourceTableName, whereExpression));


        /// <summary>
        /// 软删除
        /// </summary>
        /// <typeparam name="TType">动态类型</typeparam>
        /// <param name="model">动态实体</param>
        /// <param name="whereExpression">判断条件</param>
        /// <returns></returns>
        public virtual async Task<ResultMo> DeleteSoft<TType>(TType model = null,
            Expression<Func<TType, bool>> whereExpression = null) where TType : BaseModel
        {
            model.IsDeleted = true;
            return await ExecuteWriteAsync(connection =>
                connection.UpdatePartail(model, m => new {m.IsDeleted}, SourceTableName, whereExpression));
        }

        /// <summary>
        /// 获取单个实体对象
        /// </summary>
        /// <typeparam name="TType">动态类型</typeparam>
        /// <param name="model">动态实体类</param>
        /// <param name="whereExpression">判断条件</param>
        /// <returns></returns>
        public virtual async Task<ResultMo<TType>> Get<TType>(TType model = null, Expression<Func<TType, bool>> whereExpression = null) where TType : class => await ExecuteReadAsync(connection => connection.Get(SourceTableName, model, whereExpression));

        /// <summary>
        /// 列表查询
        /// </summary>
        /// <typeparam name="TType">动态类型</typeparam>
        /// <param name="selectSql">查询语句，包含排序等</param>
        /// <param name="totalSql">查询数量语句，不需要排序</param>
        /// <param name="parameters">参数</param>
        /// <returns></returns>
        protected internal async Task<PageListMo<TType>> GetList<TType>(string selectSql, string totalSql,
            Dictionary<string, object> parameters) where TType : class, new()
        {
            return await ExecuteReadResultAsync(async connection =>
            {
                var parameter = new DynamicParameters(parameters);
                var total = await connection.ExecuteScalarAsync<long>(totalSql, parameter);
                var list = await connection.QueryAsync<TType>(selectSql, parameter);
                return new PageListMo<TType>(total, list.ToList());
            });
        }

        #endregion
    }
}